
#include <iostream>
#include <climits>

#include <glog/logging.h>

#include <QtGlobal>

#include "octopod.h"
#include "socket.h"


namespace octopod {

const char *NetworkAddress::validchars = "0123456789abcdef.:";
static int validchars_len = strlen(NetworkAddress::validchars);

bool NetworkAddress::validAddress(const std::string &addr) {
  const char *str = addr.data();
  int len = addr.size();

  // make a table of valid chars.
  bool validtable[256];
  for (int i = 0; i < 256; i++) {
    validtable[i] = false; 
  }

  for (int i = 0; i < validchars_len; i++) {
    validtable[(int) validchars[i]] = true;
  }

  for (int i = 0; i < len; i++) {
    if (!validtable[(int) str[i]]) {
      DLOG(INFO) << Q_FUNC_INFO << "address " << addr << " isn't a valid IP address.";
      return false; 
    }
  }

  return true;
}

bool NetworkAddress::set_hostaddr(const std::string &hostaddr) { 
  if (validAddress(hostaddr)) {
    hostaddr_ = hostaddr;
    return true;
  }

  return false;
}

bool NetworkAddress::set_nataddr(const std::string &nataddr) { 
  if (validAddress(nataddr)) {
    nataddr_ = nataddr;
    return true;
  }

  return false;
}

// Calls send repeatedly until the whole string is sent.
int Socket::send(const std::string &data) {
  CHECK(data.size() <= INT_MAX);
  int total_sent = 0;

  int data_size = data.size();
  while (total_sent < data_size) {
    int sent = 0;
    int send_amount = data_size - total_sent;

    sent = send(data.data() + total_sent, send_amount);

    if (sent < 0) {
      DLOG(WARNING) << Q_FUNC_INFO << "send failed in the middle of a string send.";
      return total_sent;
    }

    LOG_IF_EVERY_N(INFO, (sent == 0), 10) << Q_FUNC_INFO << "send buffer may be full, spinning a lot.";

    total_sent += sent;
  }

  return total_sent;
}

// Calls recv repeatedly until the whole string is read.
int Socket::recv(int size, std::string *data) {
  const int kReadBlockSize = 32768;
  char buf[kReadBlockSize];
  int total_read = 0;

  data->clear();

  while (total_read < size) {
    int read = 0;
    int read_amount = kReadBlockSize;

    if (read_amount > size - total_read) {
      read_amount = size - total_read;
    }

    read = recv(read_amount, buf);

    if (read < 0) {
      DLOG(WARNING) << Q_FUNC_INFO << "recv failed in the middle of a string read.";
      return total_read;
    }

    LOG_IF_EVERY_N(INFO, (read == 0), 10) << Q_FUNC_INFO << "read buffer may be empty, spinning a lot.";

    data->append(buf, read);
    total_read += read;
  }

  return total_read;
}

}  // namespace octopod
